package liming.ai.tool;

import liming.tool.file.FileRW;
import liming.tool.json.FileConfig;
import liming.tool.json.ToJson;
import liming.tool.json.ValueOf;
import liming.tool.json.annotation.JsonMark;
import liming.tool.runnable.HandRunnable;
import liming.tool.runnable.HaveReturnValueInterface;
import org.json.JSONObject;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 自动注册父类，用于在注册中心实现自动注册
 */
public class AutoRegistration implements ToJson {
    {
        if (!getDefaultClass().isInstance(this))
            throw new RuntimeException("AutoRegistration defaultClass is not instance of this class");
        methodMap = new HashMap<>();
        ToolRegistrationCenter.registerTool(this);
    }

    protected final Map<String, MethodInfo> methodMap;

    public AutoRegistration() {
    }

    @Override
    public JSONObject serializeBody(Class<?> type) {
        return ToJson.super.serializeBody(type);
    }

    public boolean manageTool(ManageState type) {
        if (type == ManageState.Enable)
            return loadMethod(getConfig());
        return true;
    }

    public boolean manageMethod(String methodName, ManageState type) {
        if (methodMap.size() == 1) for (MethodInfo value : methodMap.values()) return value.menage(type);
        else if (methodMap.containsKey(methodName)) return methodMap.get(methodName).menage(type);
        return false;
    }

    protected boolean loadMethod(JSONObject config) {
        Class<?> defaultClass = getDefaultClass();
        for (Method method : defaultClass.getMethods()) {
            loadMethod(methodMap, method);
        }
        return true;
    }

    protected void loadMethod(Map<String, MethodInfo> methodMap, Method method) {
        if (!method.isAnnotationPresent(ToolMethod.class))
            return;
        ToolMethod toolMethod = method.getAnnotation(ToolMethod.class);
        String name = toolMethod.name();
        if (name.isEmpty())
            name = method.getName();
        MethodInfo methodInfo = loadMethod(method, name, toolMethod.describe());
        methodMap.put(name, methodInfo);
    }
    protected MethodInfo loadMethod(Method method, String name,String describe) {
        return new MethodInfo(this, method, name, describe);
    }

    protected final JSONObject getConfig() {
        return FileConfig.getConfig(getJsonRootName());
    }

    public InvokeInfo invoke(String methodName, Object... args) {
        if (methodMap.size() == 1) for (MethodInfo value : methodMap.values()) return value.invoke(args);
        else if (methodMap.containsKey(methodName)) return methodMap.get(methodName).invoke(args);
        return new InvokeInfo(getJsonRootName(), methodName, args, false, null, "toolMethod not found");
    }

    protected static class MethodInfo implements ToJson {
        @JsonMark("方法名字")
        String name;
        @JsonMark("方法介绍")
        String describe;
        Method method;
        AutoRegistration object;
        @JsonMark("状态")
        ManageState manageState = ManageState.Enable;

        public MethodInfo(AutoRegistration object, Method method, String name, String describe) {
            this.object = object;
            this.method = method;
            this.name = name;
            this.describe = describe;
        }

        public boolean menage(ManageState type) {
            this.manageState = type;
            return true;
        }

        public InvokeInfo invoke(Object... args) {
            if (manageState == ManageState.Enable)
                return HandRunnable.run(new HaveReturnValueInterface<InvokeInfo>() {
                    @Override
                    public InvokeInfo run() throws Exception {
                        List<Object> arg = new ArrayList<>();//重构参数列表
                        int index = 0;
                        int parameterCount = method.getParameterCount();
                        for (Class<?> parameterType : method.getParameterTypes()) {//使用类型自动转化
                            if (index < parameterCount)
                                arg.add(ValueOf.valueOf(parameterType, args[index++]));
                            else
                                arg.add(ValueOf.getDefault(parameterType));
                        }
                        Object[] array = arg.toArray(new Object[0]);//参数列表
                        if (!method.isAccessible())
                            method.setAccessible(true);
                        Object invoke = method.invoke(object, array);//调用方法
                        if (method.isAccessible())
                            method.setAccessible(false);
                        return new InvokeInfo(name, method.getName(), array, true, invoke, "");
                    }

                    @Override
                    public InvokeInfo onException(Exception e) {
                        if (method.isAccessible())
                            method.setAccessible(false);
                        return new InvokeInfo(name, method.getName(), args, false, ValueOf.getDefault(method.getReturnType()), FileRW.getStackTrace(e));
                    }
                });
            return new InvokeInfo(name, method.getName(), args, false, ValueOf.getDefault(method.getReturnType()), "方法已禁用");
        }
    }
}
